package com.palmgo.utils.drawer.basic;


import java.awt.Font;
import java.awt.FontMetrics;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;

import com.palmgo.utils.drawer.model.RectF;
import com.palmgo.utils.drawer.model.TrafficSegment;
import com.palmgo.utils.drawer.model.Vetrux;

/**
 * 绘制图像的工具类
 * 
 * @author tanqiyong
 *
 */
public class TrafficGraphic {
	private static Logger LOG = LoggerFactory.getLogger(DeformationAgientOld.class);
	   
	/** 距离过滤条件 */
	protected static float TWO_POINT_GAP = 5f;
	protected static float scale = 1;

	/**
	 * 根据图片区域的实际大小对过滤条件进行更改
	 */
	protected static void setGap(float _scale) {
		scale = _scale;
	}

	protected static enum segment_outline_type_e {
		up_segment_outline, down_segment_outline, total_segment_outline,
	}

	protected static enum segment_end_type_e {
		square_end_t, arrow_end_t, triangle_end_t,
	}

	protected static enum line_cap_type_e {
		total_cap_t, last_cap_t, next_cap_t
	}

	protected static void calc_traffic_segment_outline(List<Vetrux> road_line,
			List<TrafficSegment> tsb, segment_outline_type_e type, int width,
			segment_end_type_e segment_end_t) {
		int i, from = 0, to;
		// 去掉距离近的点,并修改TrafficSegment的节点数据
		rebuild_trafic_segment(road_line, tsb);
		// 处理完整个数据集，按TrafficSegment来封装
		int size = tsb.size();
		from = 0;
		for (int j = 0; j < size; j++) {
			TrafficSegment segment = tsb.get(j);
			to = segment.vertex_count;
			if (from==to){
				//当前节点没有绘制信息
				tsb.remove(j);
				j--;
				size--;
				continue;
			}
			calc_segment_outline(road_line, from, to, type,
					segment.downOutLine, width, segment_end_t);
			from = to;
		}
	}

	/**
	 * m_vectors去重
	 * 
	 * @param road_line
	 * @param tsb
	 */
	protected static void rebuild_trafic_segment(List<Vetrux> road_line,
			List<TrafficSegment> tsb) {
		int i, j;
		// tsb游标
		int to = 0;
		// 需要保存下来的点
		List<Vetrux> cache = new ArrayList<Vetrux>();
		// 将第一个元素插入集合
		cache.add(road_line.get(0));
		for (i = 1; i < road_line.size(); i++) {
			// 如果两个点距离太近，忽视其中部分点
			if (get_distance(cache.get(cache.size() - 1), road_line.get(i)) < TWO_POINT_GAP
					* scale) {
				// 如果两点距离过近
				// 如果节点数>0
				// 原数据中vertex_count:8对应的road_line位置也是8，所以，循环到这个位置时，表示当前节点结束，需要修改节点对应的vertex_count
				if (tsb.size() > 0 && tsb.get(to).vertex_count == i) {
					tsb.get(to).vertex_count = cache.size() - 1;
					to++;
				}
				continue;
			} else {
				cache.add(road_line.get(i));
				if (tsb.size() > 0 && tsb.get(to).vertex_count == i) {
					tsb.get(to).vertex_count = cache.size() - 1;
					to++;
				}
			}
		}
		road_line.clear();
		road_line.addAll(cache);

	}

	protected static double get_distance(Vetrux p1, Vetrux p2) {
		return Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y)
				* (p1.y - p2.y));
	}

	protected static int calc_segment_outline(List<Vetrux> vc, int from,
			int to, segment_outline_type_e type, List<Vetrux> vc_outLine,
			int width, segment_end_type_e segment_end_t) {
		int i, count;
		List<Vetrux> vc_o = new ArrayList<Vetrux>();
		// 如果数据不对，返回错误码
		count = vc.size();
		if (count < 2) {
			return 10001;
		} else if (count < to + 1) {
			return 10002;
		} else if (from >= to) {
			return 10003;
		}
		vc_outLine.clear();
		width = width / 2;
		// start work
		if (type == segment_outline_type_e.down_segment_outline
				|| type == segment_outline_type_e.total_segment_outline) {
			if (count == to + 1) {
				//last one
				calc_cap(vc_o, vc.get(to), vc.get(to - 1), width,
						line_cap_type_e.next_cap_t, segment_end_t);
				vc_outLine.addAll(vc_o);
			} else {
				i = to;
				calc_join(vc_o, vc.get(i + 1), vc.get(i), vc.get(i - 1), width,
						line_cap_type_e.next_cap_t);
				vc_outLine.addAll(vc_o);
			}
			// middle
			for (i = to - 1; i >= from + 1; i--) {
				calc_join(vc_o, vc.get(i + 1), vc.get(i), vc.get(i - 1), width,
						line_cap_type_e.total_cap_t);
				vc_outLine.addAll(vc_o);
			}
			// head
			if (from == 0) {
				calc_cap(vc_o, vc.get(0), vc.get(1), width,
						line_cap_type_e.last_cap_t,
						segment_end_type_e.square_end_t);
				vc_outLine.addAll(vc_o);
			} else {
				i = from;
				calc_join(vc_o, vc.get(i + 1), vc.get(i), vc.get(i - 1), width,
						line_cap_type_e.last_cap_t);
				vc_outLine.addAll(vc_o);
			}
		}
		if (type == segment_outline_type_e.down_segment_outline|| type == segment_outline_type_e.total_segment_outline) {
			// center line
			for (i = from; i <= to; i++) {
				vc_outLine.add(vc.get(i));
			}
		}
		vc_outLine.add(vc_outLine.get(0));
		return 0;
	}

	protected static void calc_cap(List<Vetrux> vc, Vetrux v0, Vetrux v1,
			double width, line_cap_type_e lin_cap_t,
			segment_end_type_e segment_end_t) {
		double len = get_distance(v0, v1);
		vc.clear();
		// 三角函数模型:dx,dy是基于坐标点v0,v1的偏移量
		double dx = width * (v1.y - v0.y) / len;
		double dy = width * (v1.x - v0.x) / len;
		if (lin_cap_t == line_cap_type_e.last_cap_t) {
			vc.add(new Vetrux( (v0.x - dx),  (v0.y + dy)));
			if (segment_end_t == segment_end_type_e.arrow_end_t) {
				vc.add(new Vetrux( (v0.x - 1.5 * dx),
						 (v0.y + 1.5 * dy)));
				vc.add(new Vetrux( (v0.x - 2.0 * dy),
						 (v0.y - 2.0 * dx)));
			} else if (segment_end_t == segment_end_type_e.triangle_end_t) {
				vc.add(new Vetrux( (v0.x - 0.5 * dx - 0.5 * dy),
						 (v0.y + 0.5 * dy - 0.5 * dx)));
			}
		} else if (lin_cap_t == line_cap_type_e.next_cap_t) {
			if (segment_end_t == segment_end_type_e.arrow_end_t) {
				vc.add(new Vetrux( (v0.x - 2.0 * dy),
						 (v0.y - 2.0 * dx)));
				vc.add(new Vetrux( (v0.x + 1.5 * dx),
						 (v0.y - 1.5 * dy)));
			} else if (segment_end_t == segment_end_type_e.triangle_end_t) {
				vc.add(new Vetrux( (v0.x + 0.5 * dx - 0.5 * dy),
						 (v0.y - 0.5 * dy - 0.5 * dx)));
			}
			vc.add(new Vetrux( (v0.x + dx),  (v0.y - dy)));
		} else if (lin_cap_t == line_cap_type_e.total_cap_t) {
			vc.add(new Vetrux( (v0.x - dx),  (v0.y + dy)));
			if (segment_end_t == segment_end_type_e.arrow_end_t) {
				vc.add(new Vetrux( (v0.x - 1.5 * dx),
						 (v0.y + 1.5 * dy)));
				vc.add(new Vetrux( (v0.x - 2.0 * dy),
						 (v0.y - 2.0 * dx)));
				vc.add(new Vetrux( (v0.x + 1.5 * dx),
						 (v0.y - 1.5 * dy)));
			} else if (segment_end_t == segment_end_type_e.triangle_end_t) {
				vc.add(new Vetrux( (v0.x - dy),  (v0.y - dx)));
			}
			vc.add(new Vetrux( (v0.x + dx),  (v0.y - dy)));
		}
	}

	protected static void calc_join(List<Vetrux> vc, Vetrux v0, Vetrux v1,
			Vetrux v2, double width, line_cap_type_e line_cap_t) {
		double len1 = get_distance(v0, v1);
		double len2 = get_distance(v1, v2);
		double dx1 = width * (v1.y - v0.y) / len1;
		double dy1 = width * (v1.x - v0.x) / len1;
		double dx2 = width * (v2.y - v1.y) / len2;
		double dy2 = width * (v2.x - v1.x) / len2;
		double xi = 0, yi = 0;
		vc.clear();
		double cp = cross_product(v0, v1, v2);
		if (cp != 0 && cp > 0 && width > 0) {
			double limit = (len1 < len2) ? len1 : len2;
			// inner join
			if (limit < width) {
				limit = width * 2;
			}
			Vetrux pt = new Vetrux();
			if (calc_intersection(v0, v1, v2, dx1, dy1, dx2, dy2, pt)) {
				if (get_distance(v1, pt) < limit * 2) {
					vc.add(pt);
				} else {
					vc.add(v1);
				}
			} else {
				// abort
			}
		} else {
			// outer join
			double dx = (dx1 + dx2) / 2;
			double dy = (dy1 + dy2) / 2;
			double dbevel = Math.sqrt(dx * dx + dy * dy);
			if ((width - dbevel) < width / 1024.0) {
				Vetrux pt = new Vetrux();
				if (calc_intersection(v0, v1, v2, dx1, dy1, dx2, dy2, pt)) {
					vc.add(pt);
				} else {
					vc.add(new Vetrux( (v1.x + dx1),
							 (v1.y - dy1)));
				}
				return;
			}
			calc_arc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2, width, line_cap_t);
		}
	}

	protected static double cross_product(Vetrux p1, Vetrux p2, Vetrux p) {
		return (p.x - p2.x) * (p2.y - p1.y) - (p.y - p2.y) * (p2.x - p1.x);
	}

	protected static boolean calc_intersection(Vetrux v0, Vetrux v1, Vetrux v2,
			double dx1, double dy1, double dx2, double dy2, Vetrux callback) {
		double ax = v0.x + dx1;
		double ay = v0.y - dy1;
		double bx = v1.x + dx1;
		double by = v1.y - dy1;
		double cx = v1.x + dx2;
		double cy = v1.y - dy2;
		double dx = v2.x + dx2;
		double dy = v2.y - dy2;
		double num = (ay - cy) * (dx - cx) - (ax - cx) * (dy - cy);
		double den = (bx - ax) * (dy - cy) - (by - ay) * (dx - cx);
		if (Math.abs(den) < 1.0e-30) {
			return false;
		}
		double r = num / den;
		callback.x =  (ax + r * (bx - ax));
		callback.y =  (ay + r * (by - ay));
		return true;
	}

	protected static void calc_arc(List<Vetrux> vc, double x, double y,
			double dx1, double dy1, double dx2, double dy2, double width,
			line_cap_type_e line_cap_t) {
		double a1 = Math.atan2(dy1, dx1);
		double a2 = Math.atan2(dy2, dx2);
		double da = 0.1;
		double Pi = Math.PI;
		int i, n = 0;
		if ((line_cap_t == line_cap_type_e.total_cap_t)
				|| (line_cap_t == line_cap_type_e.last_cap_t)) {
			vc.add(new Vetrux( (x + dx1),  (y + dy1)));
		}
		if (a1 > a2) {
			a2 += 2 * Math.PI;
		}
		n =  (int)((a2 - a1) / da);
		da = (a2 - a1) / (n + 1);
		a1 += da;
		if (line_cap_t == line_cap_type_e.total_cap_t
				|| line_cap_t == line_cap_type_e.last_cap_t) {
			for (i = 0; i < n / 2; i++) {
				vc.add(new Vetrux( (x + Math.cos(a1 + i * da) * width),
						 (y + Math.sin(a1 + i * da) * width)));
			}
		}
		if (line_cap_t == line_cap_type_e.next_cap_t
				|| line_cap_t == line_cap_type_e.last_cap_t) {
			vc.add(new Vetrux( (x + Math.cos((a1 + a2) / 2) * width),
					 (y + Math.sin((a1 + a2) / 2) * width)));
		}
		if (line_cap_t == line_cap_type_e.total_cap_t
				|| line_cap_t == line_cap_type_e.next_cap_t) {
			for (i = n / 2; i < n; i++) {
				vc.add(new Vetrux( (x + Math.cos(a1 + i * da) * width),
						 (y + Math.sin(a1 + i * da) * width)));
			}
			vc.add(new Vetrux( (x + dx2),  (y + dy2)));
		}
	}

	/**
	 * 绘制路段路况
	 * 
	 * @param context
	 * @param tsb
	 */
	protected static void draw_traffic_flow(GraphicsContext context,
			List<TrafficSegment> tsb) {
		for (int i = 0; i < tsb.size(); i++) {
			draw_segment_func(context, tsb.get(i).upOutLine,
					tsb.get(i).up_traffic_status);
			draw_segment_func(context, tsb.get(i).downOutLine,
					tsb.get(i).down_traffic_status);
			draw_segment_func(context, tsb.get(i).totalOutLine,
					tsb.get(i).traffic_status);
		}
	}

	protected static int draw_segment_func(GraphicsContext context, List<Vetrux> outline,
			String traffic_status) {
		try {
			if (outline == null || outline.size() == 0) {
				return -1;
			}
			if ("C".equals(traffic_status)) {
				context.setFill(Color.color(0, 1, 0));
			} else if ("B".equals(traffic_status)) {
				context.setFill(Color.color(1, 1, 0));
			} else if ("A".equals(traffic_status)) {
				context.setFill(Color.color(1, 0, 0));
			} else {
				context.setFill(Color.color(0, 1, 0));
			}
			context.setStroke(Color.color(1, 1, 1));
			context.beginPath();
			context.moveTo(outline.get(0).x, outline.get(0).y);
			for (int i = 1; i < outline.size(); i++) {
				context.lineTo(outline.get(i).x, outline.get(i).y);
			}
			context.closePath();
			//画线
			context.fill();
			//画边框
			context.stroke();

		} catch (Exception e) {
			e.printStackTrace();
		}
		return 0;
	}

	protected static int draw_traffic_text(GraphicsContext context, List<Vetrux> tso,
			List<TrafficSegment> tsb, RectF showRect, double lineWidth,
			float fontSize) {
		int count = 0;
		List<Vetrux> outline = new ArrayList<Vetrux>();
		List<RectF> rects = new ArrayList<RectF>();
		if (tsb == null || tsb.size() == 0) {
			return -1;
		}
		// TODO
		calc_outline(tso, outline, lineWidth, segment_end_type_e.square_end_t);
		List<TrafficText> ttb = new ArrayList<TrafficText>();
		rebuild_traffic_text(tso, tsb, ttb);
		context.setFont(javafx.scene.text.Font.font("宋体",fontSize));
		count += draw_traffic_text_0(context, ttb, 0, lineWidth, fontSize,
				showRect, outline, rects);
		count += draw_traffic_text_0(context, ttb, 1, lineWidth, fontSize,
				showRect, outline, rects);
		count += draw_traffic_text_1(context, ttb, 0, ttb.size() - 1, lineWidth,
				fontSize, showRect, outline, rects);
		return count;
	}

	/**
	 *
	 * @param context
	 * @param text
	 * @param fontsize
	 * @param width
	 * @param orient
	 * @param rbox
	 * @param markPoint
	 * @param type
	 * @return
	 */
	protected static int draw_text_func(GraphicsContext context, String text,
			float fontsize, double width, int orient, RectF rbox,
			Vetrux markPoint, int type) {
		context.setStroke(Color.WHITE);
		context.setFill(Color.WHITE);
		Vetrux point = new Vetrux(rbox.left, rbox.top);
		// 获取文字宽度
//		point.x = rbox.left;
//		point.x = (point.x-fontsize/4);
		// 获取文字高度
		Font font = new Font("宋体",Font.PLAIN,(int)fontsize);
		FontMetrics metrics= sun.font.FontDesignMetrics.getMetrics(font);
		System.out.println(metrics.getHeight()+"/"+metrics.getWidths().length);
		double dy = metrics.getHeight();
		point.y += 0;
		if (orient == 0) {
			// 横向绘制文本
			context.fillText(text, point.x, rbox.bottom);
		} else {
			// 纵向绘制
			double dix = (rbox.bottom-rbox.top)/text.length();
			for (int i = 0; i < text.length(); i++) {
				context.fillText(text.substring(i, i + 1), point.x, rbox.top+dix*i+dix);
//				point.y += fontsize/2;
			}

		}
		double mark_size = 8.0;
		List<Vetrux> vc = new ArrayList<Vetrux>();
		//绘制三角标记
		//保存当前状态
		context.save();
		get_textmark(vc, markPoint, width, mark_size, type, 2);
		int nCount = vc.size();
		context.moveTo(vc.get(0).x, vc.get(0).y);
		for (int i = 1; i < nCount; i++) {
			context.lineTo(vc.get(i).x, vc.get(i).y);
		}
		context.closePath();
		context.stroke();
		//重置
		context.restore();
		return 0;
	}

	private static void get_textmark(List<Vetrux> vc, Vetrux point,
			double width, double mark_size, int type, int g_gap) {
		Vetrux p1 = new Vetrux();
		Vetrux p2 = new Vetrux();
		Vetrux p3 = new Vetrux();
		g_gap -= 2;
		vc.clear();
		if (type == 0 || type == 6) {
			p1.x =  (point.x + width - mark_size + g_gap);
			p1.y = point.y;
			vc.add(p1);
			p2 = new Vetrux(p1.x, p1.y);
			p2.x += mark_size;
			p2.y -= mark_size / 2;
			vc.add(p2);
			p3 = new Vetrux(p2.x, p2.y);
			p3.y += mark_size;
			vc.add(p3);
		} else if (type == 1 || type == 7) {
			p1.x =  (point.x - width + mark_size - g_gap);
			p1.y = point.y;
			vc.add(p1);
			p2 = new Vetrux(p1.x, p1.y);
			p2.x -= mark_size;
			p2.y -= mark_size / 2;
			vc.add(p2);
			p3 = new Vetrux(p2.x, p2.y);
			p3.y += mark_size;
			vc.add(p3);
		} else if (type == 2 || type == 5) {
			p1.x = point.x;
			p1.y =  (point.y + width - mark_size + g_gap);
			vc.add(p1);
			p2 = new Vetrux(p1.x, p1.y);
			p2.x -= mark_size / 2;
			p2.y += mark_size;
			vc.add(p2);
			p3 = new Vetrux(p2.x, p2.y);
			p3.x += mark_size;
			vc.add(p3);
		} else if (type == 3 || type == 4) {
			p1.x = point.x;
			p1.y =  (point.y - width + mark_size - g_gap);
			vc.add(p1);
			p2 = new Vetrux(p1.x, p1.y);
			p2.x -= mark_size / 2;
			p2.y -= mark_size;
			vc.add(p2);
			p3 = new Vetrux(p2.x, p2.y);
			p3.x += mark_size;
			vc.add(p3);
		}
	}

	static int draw_traffic_text_0(GraphicsContext context, List<TrafficText> ttb,
			int number, double width, double font_size, RectF rbox,
			List<Vetrux> outline, List<RectF> rects) {
		String p;
		int orient = 0;
		RectF rtext = new RectF();
		RectF r = new RectF();
		Vetrux point;
		if (number > 0) {
			if (ttb.size() == 1) {
				return 0;
			}
			p = ttb.get(ttb.size() - 1).text;
			point = ttb.get(ttb.size() - 1).point;
		} else {
			p = ttb.get(0).text;
			point = ttb.get(0).point;
		}
		for (int i = 0; i < 8; i++) {
			orient = get_textbox(rtext, p, point, width + 2, font_size*1, i, 2);
			r.left = rtext.left;
			r.top = rtext.top;
			r.right = rtext.right;
			r.bottom = rtext.bottom;
			if (!hittest_rect(r, rbox)) {
				continue;
			}
			if (hittest_polygon(r, outline)) {
				continue;
			}
			if (hittest_rects(r, rects)) {
				continue;
			}
			rects.add(rtext);
			// draw
			draw_text_func(context, p, (float) font_size, width, orient, rtext,
					point, i);
			return 1;
		}
		return 0;
	}

	static int draw_traffic_text_1(GraphicsContext context, List<TrafficText> ttb,
			int head, int tail, double width, double font_size, RectF rbox,
			List<Vetrux> outline, List<RectF> rects) {
		String p;
		int count, number, orient = 0;
		RectF rtext = new RectF();
		RectF r = new RectF();
		Vetrux point;
		count = 0;
		number = (head + tail) / 2;
		if (number > 0) {
			// if (ttb.size() == 1) {
			// return 0;
			// }
			p = ttb.get(number).text;
			point = ttb.get(number).point;
		} else {
			return 0;
		}
		int type;
		for (int i = 0; i < 8; i++) {
			type = i;
			if (number % 2 > 0) {
				orient = get_textbox(rtext, p, point, width, font_size, type, 2);
			} else {
				type = 3 - type;
				orient = get_textbox(rtext, p, point, width, font_size, type, 2);
			}
			r.left = rtext.left;
			r.top = rtext.top;
			r.right = rtext.right;
			r.bottom = rtext.bottom;
			if (!hittest_rect(r, rbox)) {
				continue;
			}
			if (hittest_polygon(r, outline)) {
				continue;
			}
			if (hittest_rects(r, rects)) {
				continue;
			}
			rects.add(rtext);
			// draw
			draw_text_func(context, p, (float) font_size, width, orient, rtext,
					point, type);
			count = 1;
			break;
		}
		if (number - head > 1) {
			count += draw_traffic_text_1(context, ttb, head, number, width,
					font_size, rbox, outline, rects);
		}
		if (tail - number > 1) {
			count += draw_traffic_text_1(context, ttb, number, tail, width,
					font_size, rbox, outline, rects);
		}
		return count;
	}

	static void calc_outline(List<Vetrux> vc_old, List<Vetrux> vc_outline,
			double width, segment_end_type_e segment_end_t) {
		List<Vetrux> vc_o = new ArrayList<Vetrux>();
		List<Vetrux> vc = new ArrayList<Vetrux>();
		List<TrafficSegment> tsb_new = new ArrayList<TrafficSegment>();
		vc_outline.clear();
		vc.addAll(vc_old);
		rebuild_trafic_segment(vc, tsb_new);
		int count = vc.size();
		if (count < 2) {
			return;
		}
		int i;
		width = width / 2;
		calc_cap(vc_o, vc.get(0), vc.get(1), width,
				line_cap_type_e.total_cap_t, segment_end_t);
		vc_outline.addAll(vc_o);
		for (i = 1; i < count - 1; i++) {
			calc_join(vc_o, vc.get(i - 1), vc.get(i), vc.get(i + 1), width,
					line_cap_type_e.total_cap_t);
			vc_outline.addAll(vc_o);
		}
		calc_cap(vc_o, vc.get(count - 1), vc.get(count - 2), width,
				line_cap_type_e.total_cap_t, segment_end_t);
		vc_outline.addAll(vc_o);
		for (i = count - 2; i > 0; i--) {
			calc_join(vc_o, vc.get(i + 1), vc.get(i), vc.get(i - 1), width,
					line_cap_type_e.total_cap_t);
			vc_outline.addAll(vc_o);
		}
		vc_outline.add(vc_outline.get(0));
	}

	static void rebuild_traffic_text(List<Vetrux> tso,
			List<TrafficSegment> tsb, List<TrafficText> ttb) {
		for (int i = 0; i < tsb.size(); i++) {
			if (i == 0) {
				TrafficText start = new TrafficText();
				start.text = tsb.get(0).startText;
				start.point = tso.get(0);
				if (!isEmpty(start.text)) {
					ttb.add(start);
				}
			}
			TrafficText t = new TrafficText();
			t.text = tsb.get(i).endText;
			t.point = tso.get(tsb.get(i).vertex_count);
			if (!isEmpty(t.text)) {
				if (ttb.size() > 0
						&& get_distance(t.point, ttb.get(ttb.size() - 1).point) < TWO_POINT_GAP
								* scale) {
					continue;
				}
				ttb.add(t);
			}
		}
	}

	static int get_textbox(RectF rtext, String p, Vetrux point, double width,
			double font_size, int type, double g_gap) {
		int nWordNum = p.length();
		switch (type) {
		case 0:
			rtext.left = (float) (point.x + width + g_gap);
			rtext.top = (float) (point.y - font_size / 2);
			rtext.right = (float) (rtext.left +  (nWordNum + 0.5)
					* font_size);
			rtext.bottom = (float) (rtext.top + font_size);
			return 0;
		case 1:
			rtext.right = (float) (point.x - width - g_gap);
			rtext.top = (float) (point.y - font_size / 2);
			rtext.left = (float) (rtext.right -  (nWordNum + 0.5)
					* font_size);
			rtext.bottom = (float) (rtext.top + font_size);
			return 0;
		case 2:
			rtext.left = (float) (point.x - font_size / 2);
			rtext.top = (float) (point.y + width + g_gap);
			rtext.right = (float) (rtext.left + font_size);
			rtext.bottom = (float) (rtext.top +  (nWordNum + 0.5)
					* font_size);
			return 1;
		case 3:
			rtext.left = (float) (point.x - font_size / 2);
			rtext.bottom = (float) (point.y - width - g_gap);
			rtext.right = (float) (rtext.left + font_size);
			rtext.top = (float) (rtext.bottom -  (nWordNum + 0.5)
					* font_size);
			return 1;
		case 4:
			rtext.left = (float) (point.x -  ((nWordNum + 0.5) * font_size) / 2);
			rtext.bottom = (float) (point.y - width - g_gap);
			rtext.right = (float) (rtext.left +  (nWordNum + 0.5)
					* font_size);
			rtext.top = (float) (rtext.bottom - font_size);
			return 0;
		case 5:
			rtext.left = (float) (point.x -  ((nWordNum + 0.5) * font_size) / 2);
			rtext.top = (float) (point.y + width + g_gap);
			rtext.right = (float) (rtext.left +  (nWordNum + 0.5)
					* font_size);
			rtext.bottom = (float) (rtext.top + font_size);
			return 0;
		case 6:
			rtext.left = (float) (point.x + width + g_gap);
			rtext.top = (float) (point.y -  ((nWordNum + 0.5) * font_size) / 2);
			rtext.right = (float) (rtext.left + font_size);
			rtext.bottom = (float) (rtext.top +  (nWordNum + 0.5)
					* font_size);
			return 1;
		case 7:
			rtext.right = (float) (point.x - width - g_gap);
			rtext.top = (float) (point.y -  ((nWordNum + 0.5) * font_size) / 2);
			rtext.left = (float) (rtext.right - font_size);
			rtext.bottom = (float) (rtext.top +  (nWordNum + 0.5)
					* font_size);
			return 1;
		default:
			break;
		}
		return 0;
	}

	static boolean hittest_rect(RectF tbox, RectF rbox) {
		RectF r = new RectF(rbox);
		RectF t = new RectF(tbox);
		normalize_rect(r);
		normalize_rect(t);
		if ((r.left > t.left) || (r.right < t.right) || (r.top < t.top)
				|| (r.bottom > t.bottom)) {
			return false;
		}
		return true;
	}

	static boolean hittest_polygon(RectF tbox, List<Vetrux> vc) {
		int ret;
		Vetrux start, end;
		if (vc.size() < 2) {
			return false;
		}
		for (int i = 0; i < vc.size() - 1; i++) {
			start = vc.get(i);
			end = vc.get(i + 1);
			ret = calc_lineclip(tbox, start, end);
			if (ret != -1) {
				return true;
			}
		}
		return (false | isBoxInPolygon(tbox, vc));
	}

	static boolean hittest_rects(RectF tbox, List<RectF> rects) {
		RectF r;
		RectF t = new RectF(tbox);
		normalize_rect(t);
		for (int i = 0; i < rects.size(); i++) {
			r = new RectF(rects.get(i));
			normalize_rect(r);
			if ((r.left > t.right) || (r.right < t.left) || (r.top < t.bottom)
					|| (r.bottom > t.top)) {
				continue;
			} else {
				return true;
			}
		}
		return false;
	}

	private static boolean isBoxInPolygon(RectF tbox, List<Vetrux> vc) {
		boolean bRet = true;
		Vetrux start = new Vetrux();
		Vetrux end = new Vetrux();
		start.x = tbox.left;
		start.y = tbox.top;
		end.x = tbox.right;
		end.y = tbox.top;
		bRet &= isLineInPolygon(start, end, vc);
		//
		start.x = end.x;
		start.y = end.y;
		end.x = tbox.right;
		end.y = tbox.bottom;
		bRet &= isLineInPolygon(start, end, vc);
		//
		start.x = end.x;
		start.y = end.y;
		end.x = tbox.left;
		end.y = tbox.bottom;
		bRet &= isLineInPolygon(start, end, vc);
		//
		start.x = end.x;
		start.y = end.y;
		end.x = tbox.left;
		end.y = tbox.top;
		bRet &= isLineInPolygon(start, end, vc);

		return bRet;
	}

	private static boolean isLineInPolygon(Vetrux start, Vetrux end,
			List<Vetrux> vc) {
		return (isPointInPolygon(start, vc) & isPointInPolygon(end, vc));
	}

	private static boolean isPointInPolygon(Vetrux pnt, List<Vetrux> vc) {
		int j = 0;
		int cnt = 0;
		int size = vc.size();
		for (int i = 0; i < size; i++) {
			j = (i == size - 1) ? 0 : j + 1;
			if ((vc.get(i).y != vc.get(j).y)
					&& (((pnt.y >= vc.get(i).y) && (pnt.y < vc.get(j).y)) || ((pnt.y >= vc
							.get(j).y) && (pnt.y < vc.get(i).y)))
					&& (pnt.x < (vc.get(j).x - vc.get(i).x)
							* (pnt.y - vc.get(i).y)
							/ (vc.get(j).y - vc.get(i).y) + vc.get(i).x)) {
				cnt++;
			}
		}
		return (cnt % 2 > 0) ? true : false;
	}

	private static int calc_lineclip(RectF rbox, Vetrux start, Vetrux end) {
		RectF r = new RectF(rbox);
		normalize_rect(r);
		if ((r.left >= r.right) || (r.top <= r.bottom)) {
			return -1;
		}
		int ret = 0;
		byte b0 = 0, b1 = 0, bQ = 0;
		// check first point
		if (start.x < r.left) {
			b0 |= 1;
		} else if (start.x > r.right) {
			b0 |= 2;
		}
		if (start.y < r.bottom) {
			b0 |= 4;
		} else if (start.y > r.top) {
			b0 |= 8;
		}
		// check second point
		if (end.x < r.left) {
			b1 |= 1;
		} else if (end.x > r.right) {
			b1 |= 2;
		}
		if (end.y < r.bottom) {
			b1 |= 4;
		} else if (end.y > r.top) {
			b1 |= 8;
		}
		Vetrux point = new Vetrux();
		while (b0 > 0 || b1 > 0) {
			if ((b0 & b1) > 0) {
				return -1;
			}
			bQ = b0 > 0 ? b0 : b1;
			if ((1 & bQ) > 0) {// TODO
				point.x = r.left;
				point.y =( start.y + (end.y - start.y) * (r.left - start.x)
						/ (end.x - start.x));
			} else if ((2 & bQ) > 0) {
				point.x = r.right;
				point.y = (start.y + (end.y - start.y) * (r.right - start.x)
						/ (end.x - start.x));
			} else if ((4 & bQ) > 0) {
				point.x = (start.x + (end.x - start.x) * (r.bottom - start.y)
						/ (end.y - start.y));
				point.y = r.bottom;
			} else if ((8 & bQ) > 0) {
				point.x = (start.x + (end.x - start.x) * (r.top - start.y)
						/ (end.y - start.y));
				point.y = r.bottom;
			}
			if (bQ == b0) {
				start.x = point.x;
				start.y = point.y;
				b0 = 0;
				if (point.x < r.left) {
					b0 |= 1;
				} else if (point.x > r.right) {
					b0 |= 2;
				}
				if (point.y < r.bottom) {
					b0 |= 4;
				} else if (point.y > r.top) {
					b0 |= 8;
				}
				ret = 1;
			} else {
				end.x = point.x;
				end.y = point.y;
				b1 = 0;
				if (point.x < r.left) {
					b1 |= 1;
				} else if (point.x > r.right) {
					b1 |= 2;
				}
				if (point.y < r.bottom) {
					b1 |= 4;
				} else if (point.y > r.top) {
					b1 |= 8;
				}
				if (ret == 1) {
					ret = 3;
				} else {
					ret = 2;
				}
			}
		}
		return ret;
	}

	private static void normalize_rect(RectF r) {
		double t;
		if (r.left > r.right) {
			t = r.left;
			r.left = r.right;
			r.right = t;
		}
		if (r.top < r.bottom) {
			t = r.top;
			r.top = r.bottom;
			r.bottom = t;
		}
	}

	private static class TrafficText {
		Vetrux point;
		String text;
	}

	private static  boolean isEmpty(String str){
		return str==null || "".equals(str) || "".equals(str.trim());
	}
}
